<?php

declare(strict_types=1);

namespace App\Middleware;

use App\Model\admin\SystemPermission;
use App\Model\admin\SystemRolePermission;
use App\Traits\Response;
use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\HttpServer\Router\Dispatched;
use Hyperf\Utils\Context;
use HyperfExt\Jwt\Exceptions\TokenExpiredException;
use HyperfExt\Jwt\JwtFactory;
use Hyperf\Di\Annotation\Inject;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;

class AuthMiddleware implements MiddlewareInterface
{
    use Response;

    /**
     * @var ContainerInterface
     */
    protected $container;

    /**
     * @var RequestInterface
     */
    protected $request;

    /**
     * @Inject
     * @var JwtFactory
     */
    private $jwtFactory;

    public function __construct(ContainerInterface $container,RequestInterface $request)
    {
        $this->container = $container;
        $this->request = $request;
    }

    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        if ($request->getRequestTarget() == '/admin/login'){
            return $handler->handle($request);
        }
        $jwt = $this->jwtFactory->make();
        try {
            $jwt->checkOrFail();
        } catch (\Exception $exception) {
            if (! $exception instanceof TokenExpiredException) {
                return $this->fail(40000,$exception->getMessage());
            }
            //  尝试自动刷新 token
            try {
                $token = $jwt->getToken();

                // 刷新token
                $new_token = $jwt->getManager()->refresh($token);

                // 解析token载荷信息
                $payload = $jwt->getManager()->decode($token, false, true);

                // 旧token加入黑名单
                $jwt->getManager()->getBlacklist()->add($payload);

                // 一次性登录，保证此次请求畅通
                auth($payload->get('guard') ?? config('auth.default.guard'))
                    ->onceUsingId($payload->get('sub'));

                return $handler->handle($request)->withHeader('authorization', 'bearer ' . $new_token);
            } catch (\Exception $exception) {
                //    Token 验证失败
                return $this->fail(40001,$exception->getMessage());
            }
        }

        $attr = $request->getAttribute(Dispatched::class);

        if (is_null($attr->handler)){
            return $this->fail(40003, $request->getRequestTarget().' Allow: '.json_encode($attr->params) ?? '接口无法访问');
        }
        $callback = $attr->handler->callback;
        $controller = explode('\\',$callback[0]) ?? 'Empty';
        $controller = $controller[count($controller) - 1];
        $action = $callback[1] ?? 'empty';

        $userInfo = auth(config('auth.default.guard'))->user();
        $roleIds = $userInfo['role_ids'];
        if (!in_array('1',$roleIds) && $userInfo['id'] != 1){
            $menuInfo = $this->container->get(SystemPermission::class)->getPermissionByUrl($controller,$action);
            $auth_ids = $this->container->get(SystemRolePermission::class)->getAuthByRoleId($roleIds);
            if (!in_array($menuInfo['id'],$auth_ids)){
                return $this->fail(40003, '没有[' . $menuInfo['title'] . ']权限',);
            }
        }
        Context::set(config('auth.default.guard').'user-info',$userInfo);
        return $handler->handle($request);
    }

}